<script lang="tsx">
import {
  ElTable,
  ElTableColumn,
  ElPagination,
  ElButton,
  ElPopover,
  ElCheckbox,
  ElCheckboxGroup,
} from 'element-plus';
import {
  Search,
  Operation,
  Download,
  UploadFilled,
  CirclePlusFilled,
} from '@element-plus/icons-vue'
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue';
import { propTypes } from '@/utils/propTypes';
import { setIndex } from './helper';
import { getSlot } from '@/utils/tsxHelper';
import type { TableProps } from './types';
import { set } from 'lodash-es';
import { Pagination, TableColumn, TableSetPropsType, TableSlotDefault } from '@/types/table';

export default defineComponent({
  // eslint-disable-next-line vue/no-reserved-component-names
  name: 'Table',
  props: {
    pageSize: propTypes.number.def(10),
    currentPage: propTypes.number.def(1),
    // 是否多选
    selection: propTypes.bool.def(false),
    // 是否所有的超出隐藏，优先级低于schema中的showOverflowTooltip,
    showOverflowTooltip: propTypes.bool.def(true),
    // 表头
    columns: {
      type: Array as PropType<TableColumn[]>,
      default: () => [],
    },
    // 展开行
    expand: propTypes.bool.def(false),
    // 是否展示分页
    pagination: {
      type: Object as PropType<Pagination>,
      default: (): Pagination | undefined => undefined,
    },
    // 仅对 type=selection 的列有效，类型为 Boolean，为 true 则会在数据更新之后保留之前选中的数据（需指定 row-key）
    reserveSelection: propTypes.bool.def(false),
    // 加载状态
    loading: propTypes.bool.def(false),
    // 是否叠加索引
    reserveIndex: propTypes.bool.def(false),
    // 对齐方式
    align: propTypes.string
      .validate((v: string) => ['left', 'center', 'right'].includes(v))
      .def('left'),
    // 表头对齐方式
    headerAlign: propTypes.string
      .validate((v: string) => ['left', 'center', 'right'].includes(v))
      .def('left'),
    data: {
      type: Array as PropType<Recordable[]>,
      default: () => [],
    },
    // 工具栏按钮配置
    toolbarButtons: {
      type: Array as PropType<Array<{ label: string; icon?: string; action: () => void }>>,
      default: () => [],
    },
    stripe: {
      type: Boolean,
      default: true,
    }
  },
  emits: ['update:pageSize', 'update:currentPage', 'register', 'column-visibility-change','onImport','onSearch','onCreate'],
  setup(props, { attrs, slots, emit, expose }) {
    const elTableRef = ref<ComponentRef<typeof ElTable>>();
    const visibleColumns = ref<Record<string, boolean>>({});

    // 初始化可见列
    onMounted(() => {
      props.columns.forEach((column) => {
        visibleColumns.value[column.field] = true;
      });
    });

    // 切换列显隐
    const toggleColumnVisibility = (field: string, visible: boolean) => {
      visibleColumns.value[field] = visible;
      emit('column-visibility-change', visibleColumns.value);
    };
    
    // 渲染工具栏
    const renderToolbar = () => {
      return (
        <div class="toolbar">
          {props.toolbarButtons.map((button) => (
            <ElButton class="tool-btn" icon={button.icon} onClick={button.action}>
              {button.label}
            </ElButton>
          ))}
          <ElButton class="tool-btn" icon={Search} onClick={onSearch}></ElButton>
          <ElPopover
            trigger="click"
            width="180px"
            v-slots={{
              default: () => (
                <ElCheckboxGroup
                  modelValue={Object.keys(visibleColumns.value).filter(
                    (key) => visibleColumns.value[key]
                  )}
                  onChange={(values: string[]) => {
                    Object.keys(visibleColumns.value).forEach((key) => {
                      visibleColumns.value[key] = values.includes(key);
                    });
                  }}
                >
                  {props.columns.map((column) => (
                    <ElCheckbox label={column.field} key={column.field}>
                      {column.label}
                    </ElCheckbox>
                  ))}
                </ElCheckboxGroup>
              ),
              reference: () => <ElButton class="tool-btn" icon={Operation} ></ElButton>,
            }}
          />
          <ElButton class="tool-btn" icon={Download} ></ElButton>
          <ElButton icon={UploadFilled} type="primary" onClick={onImport}>导入</ElButton>
          <ElButton icon={CirclePlusFilled} type="primary" onClick={onCreate}>新建</ElButton>
        </div>
      );
    };
    const onImport = ()=> {
      console.log('导入onImport')
      emit('onImport')
    }

    const onSearch = ()=> {
      emit('onSearch')
    }

    const onCreate = ()=> {
      emit('onCreate')
    } 

    // 注册
    onMounted(() => {
      const tableRef = unref(elTableRef);
      emit('register', tableRef?.$parent, elTableRef);
    });

    const pageSizeRef = ref(props.pageSize);
    const currentPageRef = ref(props.currentPage);
    const outsideProps = ref<TableProps>({});
    const mergeProps = ref<TableProps>({});

    const getProps = computed(() => {
      const propsObj = { ...props };
      Object.assign(propsObj, unref(mergeProps));
      return propsObj;
    });

    const setProps = (props: TableProps = {}) => {
      mergeProps.value = Object.assign(unref(mergeProps), props);
      outsideProps.value = props;
    };

    const setColumn = (columnProps: TableSetPropsType[], columnsChildren?: TableColumn[]) => {
      const { columns } = unref(getProps);
      for (const v of columnsChildren || columns) {
        for (const item of columnProps) {
          if (v.field === item.field) {
            set(v, item.path, item.value);
          } else if (v.children?.length) {
            setColumn(columnProps, v.children);
          }
        }
      }
    };

    const selections = ref<Recordable[]>([]);

    const selectionChange = (selection: Recordable[]) => {
      selections.value = selection;
    };

    expose({
      setProps,
      setColumn,
      selections,
    });

    const pagination = computed(() => {
      return Object.assign(
        {
          small: false,
          background: true,
          pagerCount: document.body.clientWidth < 992 ? 5 : 7,
          layout: 'total, sizes, prev, pager, next, jumper',
          pageSizes: [10, 20, 30, 50, 100],
          disabled: false,
          hideOnSinglePage: false,
          total: 10,
        },
        unref(getProps).pagination
      );
    });

    watch(
      () => unref(getProps).pageSize,
      (val: number) => {
        pageSizeRef.value = val;
      }
    );

    watch(
      () => unref(getProps).currentPage,
      (val: number) => {
        currentPageRef.value = val;
      }
    );

    watch(
      () => pageSizeRef.value,
      (val: number) => {
        emit('update:pageSize', val);
      }
    );

    watch(
      () => currentPageRef.value,
      (val: number) => {
        emit('update:currentPage', val);
      }
    );

    const getBindValue = computed(() => {
      const bindValue: Recordable = { ...attrs, ...props };
      delete bindValue.columns;
      delete bindValue.data;
      return bindValue;
    });

    const renderTableSelection = () => {
      const { selection, reserveSelection, align, headerAlign } = unref(getProps);
      return selection ? (
        <ElTableColumn
          type="selection"
          reserveSelection={reserveSelection}
          align={align}
          headerAlign={headerAlign}
          width="50"
        ></ElTableColumn>
      ) : undefined;
    };

    const renderTableExpand = () => {
      const { align, headerAlign, expand } = unref(getProps);
      return expand ? (
        <ElTableColumn type="expand" align={align} headerAlign={headerAlign}>
          {{
            // @ts-ignore
            default: (data: TableSlotDefault) => getSlot(slots, 'expand', data),
          }}
        </ElTableColumn>
      ) : undefined;
    };

    const rnderTreeTableColumn = (columnsChildren: TableColumn[]) => {
      const { align, headerAlign, showOverflowTooltip } = unref(getProps);
      return columnsChildren.map((v) => {
        const props = { ...v };
        if (props.children) delete props.children;
        return (
          <ElTableColumn
            showOverflowTooltip={showOverflowTooltip}
            align={align}
            headerAlign={headerAlign}
            {...props}
            prop={v.field}
          >
            {{
              default: (data: TableSlotDefault) =>
                v.children && v.children.length
                  ? rnderTreeTableColumn(v.children)
                  : // @ts-ignore
                    getSlot(slots, v.field, data) ||
                    v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
                    data.row[v.field],
              // @ts-ignore
              header: getSlot(slots, `${v.field}-header`),
            }}
          </ElTableColumn>
        );
      });
    };

    const rnderTableColumn = (columnsChildren?: TableColumn[]) => {
      const {
        columns,
        reserveIndex,
        pageSize,
        currentPage,
        align,
        headerAlign,
        showOverflowTooltip,
      } = unref(getProps);
      return [...[renderTableExpand()], ...[renderTableSelection()]].concat(
        (columnsChildren || columns)
          .filter((column) => visibleColumns.value[column.field])
          .map((v) => {
            if (v.type === 'index') {
              return (
                <ElTableColumn
                  type="index"
                  index={
                    v.index
                      ? v.index
                      : (index) => setIndex(reserveIndex, index, pageSize, currentPage)
                  }
                  align={v.align || align}
                  headerAlign={v.headerAlign || headerAlign}
                  label={v.label}
                  width="65px"
                ></ElTableColumn>
              );
            } else {
              const props = { ...v };
              if (props.children) delete props.children;
              return (
                <ElTableColumn
                  showOverflowTooltip={showOverflowTooltip}
                  align={align}
                  headerAlign={headerAlign}
                  {...props}
                  prop={v.field}
                >
                  {{
                    default: (data: TableSlotDefault) =>
                      v.children && v.children.length
                        ? rnderTreeTableColumn(v.children)
                        : // @ts-ignore
                          getSlot(slots, v.field, data) ||
                          v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
                          data.row[v.field],
                    // @ts-ignore
                    header: () => getSlot(slots, `${v.field}-header`) || v.label,
                  }}
                </ElTableColumn>
              );
            }
          })
      );
    };

    return () => (
      <div v-loading={unref(getProps).loading}>
        {renderToolbar()}
        <ElTable
          // @ts-ignore
          ref={elTableRef}
          data={unref(getProps).data}
          onSelection-change={selectionChange}
          {...unref(getBindValue)}
        >
          {{
            default: () => rnderTableColumn(),
            // @ts-ignore
            append: () => getSlot(slots, 'append'),
          }}
        </ElTable>
        {unref(getProps).pagination ? (
          <ElPagination
            v-model:pageSize={pageSizeRef.value}
            v-model:currentPage={currentPageRef.value}
            class="float-right mb-15px mt-15px"
            {...unref(pagination)}
          ></ElPagination>
        ) : undefined}
      </div>
    );

    
  },
});
</script>

<style lang="scss" scoped>
.toolbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 16px;
  .tool-btn{
    background: rgba(45,87,204,0.1);
    border-color: rgba(45,87,204,0.1);
    color: #2D57CC;
    border: none;
    :deep(.el-icon){
      color: #2D57CC;
    }
  }
}

:deep(.el-button.is-text) {
  padding: 8px 4px;
  margin-left: 0;
}

:deep(.el-button.is-link) {
  padding: 8px 4px;
  margin-left: 0;
}
</style>